const path = require('path')
const fs = require('fs')
const { Worker } = require('worker_threads')
const os = require('os')

let timer = null
let number = 0

// 定义一周前的时间（以毫秒为单位）
const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000

// 定义一个月前的时间（以毫秒为单位）
const oneMonthAgo = new Date()
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1)

// 插件入口
class MyCustomPlugin {
  constructor(config, options) {
    this.config = config // config.yml文件下middlewares配置
    this.options = options // verdaccio实例的配置
  }

  // 注册中间件
  register_middlewares(app, auth, storage) {
    /**
     * logs_path verdaccio 操作日志路径
     * storage_path .verdaccio-db.json本地包数据存储文件(storage可以直接register_middlewares接参storage上直接获取)
     */
    const { logs_path, storage_path, isPm2Hosting } = this.config

    // 私有化包列表
    const storagePlugin_list = storage?.localStorage?.storagePlugin?.data?.list

    // listen
    const listen_path = storage?.config?.listen

    /**
     * ### 添加自定义路由
     * @description: 自定义页面路由
     */
    app.get('/custom-download-logs', (req, res) => {
      const filePath = path.join(__dirname, 'static', 'custom-download-logs.html')
      fs.readFile(filePath, 'utf8', (err, data) => {
        if (err) {
          res.status(500).send('Error loading custom page')
        } else {
          res.send(data)
        }
      })
    })

    /**
     * ### 获取pm2 verdaccio日志接口
     * @description: verdaccio由pm2 启动, 所以需要读取pm2 verdaccio日志, 用于解析下载日志
     * 需考虑日志文件过大使用日志轮转分割存储
     */
    app.get('/api/getPm2VerdaccioLogs', async (req, res) => {
      try {
        // 获取 CPU 核心数
        const cpuCount = os.cpus().length
        number = 0
        console.log(`当前电脑 CPU ${cpuCount} 核, 即将启动多线程读取日志文件`)
        timer = setInterval(() => {
          number++
          console.log(`${number}`)
        }, 1000)

        const chunks = splitLogFile(logs_path, cpuCount)
        const data = await startWorkers(logs_path, chunks)

        res.send({
          code: 0,
          msg: '读取日志记录成功',
          data: data
        })
      } catch (error) {
        res.status(500).send({
          code: 1,
          msg: '读取日志记录失败',
          error: error.message
        })
      }
    })

    /**
     * ### 计算包下载量统计
     * @param {*} logs
     * @returns
     */
    const calculatePackageStats = (logs) => {
      const packageTotal = []
      const packageDetail = {}

      storagePlugin_list.forEach((pkg) => {
        let totalDownloads = 0

        packageDetail[pkg] = {
          total: 0,
          details: []
        }

        for (const key in logs.downloadStats) {
          if (logs.downloadStats.hasOwnProperty(key)) {
            if (key === pkg || (key.includes(pkg) && key.endsWith('.tgz'))) {
              totalDownloads += logs.downloadStats[key]
              packageDetail[pkg].details.push({
                packageName: key,
                version: key,
                value: logs.downloadStats[key],
                weekDownloads: logs.oneWeekAgoPackageDownloads[key] || 0,
                monthDownloads: logs.oneMonthAgoPackageDownloads[key] || 0
              })
            }
          }
        }

        packageDetail[pkg].total = totalDownloads
        packageTotal.push({
          packageName: pkg,
          version: '',
          value: totalDownloads,
          totalWeekDownloads: logs.oneWeekAgoPackageDownloads[pkg] || 0,
          totalMonthDownloads: logs.oneMonthAgoPackageDownloads[pkg] || 0,
          details: packageDetail[pkg].details
        })
      })

      return {
        downloadLogs: packageTotal,
        config: this.config
      }
    }
    // 将日志文件拆分为多个部分
    const splitLogFile = (logFilePath, numWorkers) => {
      const fileSize = fs.statSync(logFilePath).size
      const chunkSize = Math.ceil(fileSize / numWorkers)
      const chunks = []

      for (let i = 0; i < numWorkers; i++) {
        const start = i * chunkSize
        const end = (i + 1) * chunkSize
        chunks.push({ start, end })
      }

      return chunks
    }

    // 启动 Worker 线程
    const startWorkers = (logFilePath, chunks) => {
      return new Promise((resolve, reject) => {
        const workers = []
        const results = []
        let completedWorkers = 0

        chunks.forEach((chunk, index) => {
          const workerPath = path.resolve(__dirname, 'worker.js')
          const worker = new Worker(workerPath, {
            workerData: {
              isPm2Hosting,
              logFilePath,
              start: chunk.start,
              end: chunk.end,
              oneWeekAgo,
              oneMonthAgo
            }
          })

          worker.on('message', (result) => {
            results[index] = result
            completedWorkers++

            // 所有 Worker 线程完成后，合并结果
            if (completedWorkers === chunks.length) {
              const mergedResult = mergeResults(results)
              const packageStats = calculatePackageStats(mergedResult)

              resolve(packageStats)

              // 将结果导出为 JSON 文件
              fs.writeFileSync(path.join(__dirname, 'download_stats_total.json'), JSON.stringify(packageStats, null, 2))
              console.log('结果已导出到 download_stats_total.json')

              if (timer) {
                console.log('所有 Worker 线程已完成, 读取日志文件成功')
                clearInterval(timer)
                timer = null
              }
            }
          })

          worker.on('error', (error) => {
            console.error(`Worker ${index} 出错:`, error)
            reject(`Worker ${index} 出错:`, error)
          })

          worker.on('exit', (code) => {
            if (code !== 0) {
              console.error(`Worker ${index} 退出，代码: ${code}`)
              reject(`Worker ${index} 退出，代码: ${code}`)
            }
          })

          workers.push(worker)
        })
      })
    }

    // 合并 Worker 线程的结果
    const mergeResults = (results) => {
      const downloadStats = {}
      const oneWeekAgoPackageDownloads = {}
      const oneMonthAgoPackageDownloads = {}

      results.forEach((result) => {
        Object.entries(result.downloadStats).forEach(([packageName, count]) => {
          downloadStats[packageName] = (downloadStats[packageName] || 0) + count
        })

        Object.entries(result.oneWeekAgoPackageDownloads).forEach(([packageName, count]) => {
          oneWeekAgoPackageDownloads[packageName] = (oneWeekAgoPackageDownloads[packageName] || 0) + count
        })

        Object.entries(result.oneMonthAgoPackageDownloads).forEach(([packageName, count]) => {
          oneMonthAgoPackageDownloads[packageName] = (oneMonthAgoPackageDownloads[packageName] || 0) + count
        })
      })

      return {
        downloadStats,
        oneWeekAgoPackageDownloads,
        oneMonthAgoPackageDownloads
      }
    }

    console.log(`Plugin started successfully `)
    console.log(`Custom plugin page registered at ${listen_path}/custom-download-logs`)
  }
}

module.exports = (config, options) => new MyCustomPlugin(config, options)
